#!/usr/bin/ruby

require 'itch'
require 'test/unit'

class TestItch < Test::Unit::TestCase

	#Mock object to test that proper calls are made...
	class MockOLE
		def initialize (name='')
			@name = name
		end
		attr_accessor :calls, :name
		#Log any called methods and return a new stub that is itself an OLE object.
		def method_missing (method_id, *arguments)
			(@calls ||= Array.new) << [method_id.id2name, arguments]
			(@methods ||= Hash.new)[method_id.id2name] ||= MockOLE.new(method_id.id2name)
		end
	end

	#A mock object that returns the name of the accessor as the accessor value.
	class MockObject
		def method_missing (method_id, *arguments)
			method_id.id2name
		end
	end

	
	def setup
		#Create a Helper to run tests on.
		@helper = Itch.new(Itch.create_itunes_interface)
	end
	
	
	def test_parse_options
		
		#Test parsing.
		config = @helper.parse_options [
			'--all-playlists',
			'--update-ipod'
		]
		assert_not_nil(config['all-playlists'])
		assert_not_nil(config['update-ipod'])
		
		#Test values on options that should occur only once.
		config = @helper.parse_options [
			'--set-disc-count', '1',
			'--set-disc-count', '2', #Override previous value.
			'--set-disc-number', '1'
		]
		assert_equal(2, config['set-disc-count'])
		assert_equal(1, config['set-disc-number'])
		
		#Test default values.
		config = @helper.parse_options [
			'--volume-up', 
			'--volume-down',
			'--scan-forwards',
			'--scan-backwards'
		]
		assert_equal(10, config['volume-up'])
		assert_equal(10, config['volume-down'])
		assert_equal(10, config['scan-forwards'])
		assert_equal(10, config['scan-backwards'])
		config = @helper.parse_options [
			'--volume-up', '11',
			'--volume-down', '12'
		]
		assert_equal(11, config['volume-up'])
		assert_equal(12, config['volume-down'])
		
		#Test gathering of multiple values for same option.
		config = @helper.parse_options [
			'--playlist', 'foo',
			'--playlist', 'bar',
			'--find', 'baz',
			'--find', 'eep'
		]
		assert(config['playlist'].include?('foo'))
		assert(config['playlist'].include?('bar'))
		assert(config['find'].include?('baz'))
		assert(config['find'].include?('eep'))
		
		#Test short options.
		config = @helper.parse_options [
			'-p',
			'-v', '100',
		]
		assert_not_nil(config['play-pause'])
		assert_equal(100, config['volume'])
		
	end
	
	
	def test_set_default_options
	
		#Test setting defaults on an empty config.
		config = @helper.set_default_options
		assert(config['library'])
		
		#Test running on an existing config.
		config = @helper.set_default_options(
			{'library' => false}
		)
		assert_equal(false, config['library'])
		
		#Test using entire library by default.
		[
			{'current-playlist' => true},
			{'playlist' => ['foo', 'bar']},
			{'all-playlists' => true},
			{'create-playlist' => ['foo', 'bar']},
		].each do |options|
			config = @helper.set_default_options(options)
			assert_nil(config['library'], config.to_s)
		end
		config = @helper.set_default_options({'library' => true}) #Obviously, we shouldn't set library to false if it was explicitly specified.
		assert(config['library'])
		config = @helper.set_default_options({'library' => false}) #If library is explicitly set to false, keep it false.
		assert_equal(false, config['library'])
		
	end


	def test_create_itunes_interface
		interface = Itch.create_itunes_interface
		assert_not_nil(interface.LibraryPlaylist)
	end
	
	
	def test_get_playlists
	
		#Test that the appropriate playlist set is returned for various configs.
		playlists = @helper.get_playlists({'library' => true})
		assert_equal('Library', playlists[0].Name)
# 		playlists = @helper.get_playlists({'current-playlist' => true})
# 		assert_equal('Party Shuffle', playlists[0].Name)
		playlists = @helper.get_playlists({'all-playlists' => true})
		assert_equal('Library', playlists[0].Name)
		assert_equal('Party Shuffle', playlists[1].Name)
		playlists = @helper.get_playlists({'create-playlist' => ['foo', 'bar']})
		assert_equal('foo', playlists[0].Name)
		assert_equal('bar', playlists[1].Name)
		playlists = @helper.get_playlists({'playlist' => ['Recently Added', 'My Top Rated']})
		assert_equal('Recently Added', playlists[0].Name)
		assert_equal('My Top Rated', playlists[1].Name)
		
	end


	def test_delete_playlists
	
		#Create some temporary playlists.
		playlists = @helper.get_playlists({'create-playlist' => ['delete1', 'delete2']})
		#Test that the playlist is deleted.
		@helper.delete_playlists({'delete-playlist' => ['delete1', 'delete2']})
		assert_raise(RuntimeError) do
			@helper.get_playlists({'playlist' => ['delete1']})
		end
		assert_raise(RuntimeError) do
			@helper.get_playlists({'playlist' => ['delete2']})
		end
		#Test deleting a missing playlist.
		assert_raise(RuntimeError) do
			@helper.delete_playlists({'delete-playlist' => ['delete1']})
		end
		
	end
	
	
	def test_get_tracks
		#TODO: Re-implement using mock object.
# 		#Create a playlist to add files to.
# 		playlists = @helper.get_playlists({'create-playlist' => 'add1'})
# 		#Test that appropriate tracks are returned for various options.
# 		tracks = @helper.get_tracks({'add-file' => 'test.mp3'}, playlists)
# 		assert_equal('J-Hop', tracks[0].Name)
# 		tracks = @helper.get_tracks({'all-tracks' => true}, playlists)
# 		assert_equal('J-Hop', tracks[0].Name)
# 		tracks = @helper.get_tracks({'find' => 'J-Hop'}, playlists)
# 		assert_equal('J-Hop', tracks[0].Name)
# 		tracks = @helper.get_tracks({'visible-find' => 'J-Hop'}, playlists)
# 		assert_equal('J-Hop', tracks[0].Name)
# 		tracks = @helper.get_tracks({'find-artist' => 'Jay'}, playlists)
# 		assert_equal('J-Hop', tracks[0].Name)
# 		tracks = @helper.get_tracks({'find-album' => 'HopJ'}, playlists)
# 		assert_equal('J-Hop', tracks[0].Name)
# 		tracks = @helper.get_tracks({'find-composer' => 'MrSymphony'}, playlists)
# 		assert_equal('J-Hop', tracks[0].Name)
# 		tracks = @helper.get_tracks({'find-track-name' => 'J-Hop'}, playlists)
# 		assert_equal('J-Hop', tracks[0].Name)
	end
	
	
	def test_perform_general_operations
	
		@helper.perform_general_operations({'volume' => 50})
		assert_equal(50, @helper.interface.SoundVolume)
		@helper.perform_general_operations({'volume-up' => 10})
		assert_equal(60, @helper.interface.SoundVolume)
		@helper.perform_general_operations({'volume-down' => 9})
		assert_equal(51, @helper.interface.SoundVolume)
		
	end
	
	
	def test_perform_track_operations
	
		track = MockOLE.new
		
		#Test generic attribute setting.
		def test (attribute, track, expected_method)
			@helper.perform_track_operations({attribute => attribute}, [track])
			assert_equal([expected_method, [attribute]], track.calls.pop)
		end
		test('set-artist', track, 'Artist=')
		test('set-album', track, 'Album=')
		test('set-bpm', track, 'BPM=')
		test('set-comment', track, 'Comment=')
		test('set-composer', track, 'Composer=')
		test('set-disc-count', track, 'DiscCount=')
		test('set-disc-number', track, 'DiscNumber=')
		test('set-eq', track, 'EQ=')
		test('set-genre', track, 'Genre=')
		test('set-grouping', track, 'Grouping=')
		test('set-name', track, 'Name=')
		test('set-play-count', track, 'PlayedCount=')
		test('set-skip-count', track, 'SkippedCount=')
		test('set-track-count', track, 'TrackCount=')
		test('set-track-number', track, 'TrackNumber=')
		test('set-track-volume', track, 'VolumeAdjustment=')
		test('set-year', track, 'Year=')
		#Test specialized attribute setting.
		@helper.perform_track_operations({'set-enabled' => true}, [track])
		assert_equal(['Enabled=', [true]], track.calls.pop)
		@helper.perform_track_operations({'set-disabled' => true}, [track])
		assert_equal(['Enabled=', [false]], track.calls.pop)
		@helper.perform_track_operations({'set-rating' => 1}, [track])
		assert_equal(['Rating=', [20]], track.calls.pop) #Rating is stored as a number from 1-100 internally
		@helper.perform_track_operations({'set-rating' => 5}, [track])
		assert_equal(['Rating=', [100]], track.calls.pop)
		
	end
	
	
	def test_track_info
	
		track = MockObject.new
		
		#Test that the right attribute is returned for each tag.
		assert_equal('Artist', @helper.track_info(track, '%a'))
		assert_equal('KindAsString', @helper.track_info(track, '%e'))
		assert_equal('Album', @helper.track_info(track, '%A'))
		def track.BPM
			120
		end
		assert_equal('120', @helper.track_info(track, '%b'))
		assert_equal('Composer', @helper.track_info(track, '%c'))
		assert_equal('Comment', @helper.track_info(track, '%C'))
		def track.DiscNumber
			1
		end
		assert_equal('1', @helper.track_info(track, '%d'))
		def track.DiscCount
			2
		end
		assert_equal('2', @helper.track_info(track, '%D'))
		def track.Enabled
			1
		end
		assert_equal('enabled', @helper.track_info(track, '%E'))
		def track.Enabled
			0
		end
		assert_equal('disabled', @helper.track_info(track, '%E'))
		def track.Location
			'C:\content\Orbital\The Altogether (disc 2_ the Remixes, Unr\06 Beelzebeat.mp3'
		end
		assert_equal(track.Location, @helper.track_info(track, '%l'))
		def track.PlayedCount
			15
		end
		assert_equal('15', @helper.track_info(track, '%p'))
		assert_equal('EQ', @helper.track_info(track, '%q'))
		assert_equal('Genre', @helper.track_info(track, '%g'))
		assert_equal('Grouping', @helper.track_info(track, '%G'))
		assert_equal('Name', @helper.track_info(track, '%n'))
		def track.Rating
			100 #Internal rating is a number from 0-100.
		end
		assert_equal('5', @helper.track_info(track, '%r'))
		def track.SkippedCount
			3
		end
		assert_equal('3', @helper.track_info(track, '%s'))
		def track.TrackNumber
			2
		end
		assert_equal('2', @helper.track_info(track, '%t'))
		def track.TrackCount
			12
		end
		assert_equal('12', @helper.track_info(track, '%T'))
		assert_equal('VolumeAdjustment', @helper.track_info(track, '%v'))
		assert_equal('Year', @helper.track_info(track, '%y'))
		
		#Test multiple tags at once.
		assert_equal('Artist - Album - 2 - Name', @helper.track_info(track, '%a - %A - %t - %n'))
		
		#Test substituting % for %%.
		assert_equal('Artist%Name', @helper.track_info(track, '%a%%%n'))
		assert_equal('%Name', @helper.track_info(track, '%%%n'))
		assert_equal('Artist%', @helper.track_info(track, '%a%%'))
		assert_equal('%Album%', @helper.track_info(track, '%%%A%%'))
 		assert_equal('%', @helper.track_info(track, '%%'))
		
	end
	
	
end
